const { Readable, Writable } = require('node:stream');
const fs = require('node:fs');
const path = require('node:path');
// const __dirname = path.dirname(__filename);

// 检查文件读权限
async function checkReadPermission(filePath) {
  try {
    /**
     * fs.constants 需要使用 NODE 20 以后的版本。
     */
    await fs.promises.access(filePath, fs.promises.constants.R_OK);
    console.log('文件有读权限');
    return true;
  } catch (err) {
    console.error('文件没有读权限:', err);
    return false;
  }
}

// 检查目录写权限
async function checkWritePermission(dirPath) {
  try {
    await fs.promises.access(dirPath, fs.promises.constants.W_OK);
    console.log('目录有写权限');
    return true;
  } catch (err) {
    console.error('目录没有写权限:', err);
    return false;
  }
}

// 自定义可读流
class FileReadStream extends Readable {
  constructor(filename, options = {}) {
    super(options);
    this.filename = filename;
    this.fd = null;
    this.buffer = Buffer.alloc(1024);
    this.position = 0;
    this.isReading = false;
  }

  _construct(callback) {
    // 打开文件
    fs.open(this.filename, 'r', (err, fd) => {
      if (err) {
        callback(err);
        return;
      }
      this.fd = fd;
      callback();
    });
  }

  _read(size) {
    if (this.isReading) return;
    
    this.isReading = true;
    fs.read(this.fd, this.buffer, 0, this.buffer.length, this.position, (err, bytesRead) => {
      this.isReading = false;
      
      if (err) {
        this.destroy(err);
        return;
      }
      
      if (bytesRead === 0) {
        // 文件读取完毕
        this.push(null);
        return;
      }
      
      this.position += bytesRead;
      const shouldContinue = this.push(this.buffer.slice(0, bytesRead));
      
      if (shouldContinue) {
        this._read(size);
      }
    });
  }

  _destroy(err, callback) {
    if (this.fd && typeof this.fd === 'number') {
      fs.close(this.fd, (closeErr) => {
        this.fd = null;  // 清除文件描述符
        callback(closeErr || err);
      });
    } else {
      callback(err);
    }
  }
}

// 自定义可写流
class FileWriteStream extends Writable {
  constructor(filename, options = {}) {
    super(options);
    this.filename = filename;
    this.fd = null;
  }

  _construct(callback) {
    // 打开文件
    fs.open(this.filename, 'w', (err, fd) => {
      if (err) {
        callback(err);
        return;
      }
      this.fd = fd;
      callback();
    });
  }

  _write(chunk, encoding, callback) {
    if (!this.fd) {
      this.once('open', () => this._write(chunk, encoding, callback));
      return;
    }

    fs.write(this.fd, chunk, 0, chunk.length, null, (err, bytesWritten) => {
      if (err) {
        this.destroy(err);
        callback(err);
        return;
      }
      callback();
    });
  }

  _destroy(err, callback) {
    if (this.fd) {
      fs.close(this.fd, (closeErr) => {
        callback(closeErr || err);
      });
    } else {
      callback(err);
    }
  }
}

// 检查文件和目录权限
(async () => {
  console.log(__dirname)
  const inputFilePath = path.join(__dirname, 'input.txt');
  const outputFilePath = path.join(__dirname, 'output.txt');

  const hasReadPermission = await checkReadPermission(inputFilePath);
  const hasWritePermission = await checkWritePermission(path.dirname(outputFilePath));

  if (hasReadPermission && hasWritePermission) {
    // 创建自定义的 Readable 和 Writable 流实例
    const readStream = new FileReadStream(inputFilePath);
    const writeStream = new FileWriteStream(outputFilePath);

    // 将 Readable 流通过管道传递给 Writable 流
    readStream.pipe(writeStream);

    // 监听错误事件
    readStream.on('error', (err) => {
      console.error('读取文件时出错:', err);
    });

    writeStream.on('error', (err) => {
      console.error('写入文件时出错:', err);
    });

    writeStream.on('finish', () => {
      console.log('文件传输完成');
    });

    // 添加日志以调试
    readStream.on('open', () => {
      console.log('Readable 流已打开');
    });

    writeStream.on('open', () => {
      console.log('Writable 流已打开');
    });

    readStream.on('data', (chunk) => {
      console.log(`收到数据块: ${chunk.length} 字节`);
    });

    writeStream.on('drain', () => {
      console.log('Writable 流已清空');
    });
  }
})();
